package com.system.oauthserver.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import java.util.concurrent.TimeUnit;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration  extends AuthorizationServerConfigurerAdapter {
    //访问客户端密钥
    public static final String CLIENT_SECRET = "123456";
    //访问客户端ID
    public static final String CLIENT_ID ="client_1";

    /**
     * 用来支持password 模式
     */
    @Autowired
    AuthenticationManager authenticationManager;

    /**
     * 该对象用来完成redis缓存，将令牌信息存储到redis缓存种
     */
    @Autowired
    RedisConnectionFactory redisConnectionFactory;


    /**
     * 提供PasswordEncoder  密码加密工具
     * @return
     */
    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置password授权模式，authorizedGrantTypes表示授权模式为 password 和refresh_token 两种
     * accessTokenValiditySeconds 配置accesss_token的过期时间
     * resourceIds 配置了资源id
     * secret 配置了加密后的密码 明文是123
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        String finalSecret = new BCryptPasswordEncoder().encode(CLIENT_SECRET);
        clients.inMemory()
                .withClient(CLIENT_ID)
                .authorizedGrantTypes("password","refresh_token")
                .scopes("all")
                .secret(finalSecret);
    }

    @Bean
    public RedisTokenStore redisTokenStore() {
        return new RedisTokenStore(redisConnectionFactory);
    }

    /**
     * 配置了令牌存储 authenticationManager 和 userDetailsService 主要支持password模式和令牌的刷新
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //token信息存到redis
        endpoints.tokenStore(redisTokenStore()).authenticationManager(authenticationManager);
        //配置TokenService参数
        DefaultTokenServices tokenService = new DefaultTokenServices();
        tokenService.setTokenStore(endpoints.getTokenStore());
        tokenService.setSupportRefreshToken(true);
        tokenService.setClientDetailsService(endpoints.getClientDetailsService());
        tokenService.setTokenEnhancer(endpoints.getTokenEnhancer());
        //7天
        tokenService.setAccessTokenValiditySeconds((int)TimeUnit.DAYS.toSeconds(7));
        //14天
        tokenService.setRefreshTokenValiditySeconds((int)TimeUnit.DAYS.toSeconds(14));
        tokenService.setReuseRefreshToken(false);
        endpoints.tokenServices(tokenService);
    }

    /**
     * 支持client_id 和 client_secret 做登录认证
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允许表单认证
        security.allowFormAuthenticationForClients().tokenKeyAccess("isAuthenticated()")
                .checkTokenAccess("permitAll()");
    }
}
